package com.iwdnb.gkgz.common.utils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.google.common.collect.Lists;
import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.bmnf.common.utils.BooleanUtils;
import com.iwdnb.bmnf.common.utils.ClassUtils;
import com.iwdnb.gkgz.common.enums.StockImageProfitTypeEnums;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.TldMatchInfo;
import com.iwdnb.gkgz.common.model.dto.TldMatchInfo.TldMatchItem;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import static com.iwdnb.gkgz.common.utils.StockUtils.getAveragePrice;
import static com.iwdnb.gkgz.common.utils.StockUtils.isLimitDown;
import static com.iwdnb.gkgz.common.utils.StockUtils.isLimitUp;

@Slf4j
@Component
public class TulongdaoUtils implements ApplicationContextAware {
    private static final int fivePeriod = 5;
    public static final int sevenPeriod = 7;
    private static final int tenPeriod = 10;
    private static final int thirteenPeriod = 13;
    private static final int twentyPeriod = 20;
    private static final int thirtyPeriod = 30;
    private static final int sixtyPeriod = 60;
    public static final List<Integer> ALL_PERIODS = Lists.newArrayList(twentyPeriod, thirteenPeriod, tenPeriod,
        sixtyPeriod, thirtyPeriod);
    private static final Map<Integer, String> PERIOD_MAP = new LinkedHashMap<>();

    public static final BigDecimal ten = new BigDecimal(10);
    public static final BigDecimal ninePointSix = new BigDecimal(9.6);
    public static final BigDecimal seven = new BigDecimal(7);
    public static final BigDecimal sixPointFive = new BigDecimal(6.5);
    public static final BigDecimal five = new BigDecimal(5);

    public static final BigDecimal three = new BigDecimal(3);

    public static final BigDecimal twoPointTwoFive = new BigDecimal(2.25);
    public static final BigDecimal two = new BigDecimal(2);
    public static final BigDecimal onePointTwoFive = new BigDecimal(1.25);
    public static final BigDecimal onePointZeroOneTwo = new BigDecimal(1.012);
    public static final BigDecimal one = new BigDecimal(1);
    public static final BigDecimal zeroNineEightEight = new BigDecimal(0.988);

    public static final BigDecimal zero = new BigDecimal(0);
    public static final BigDecimal zeroPointFive = new BigDecimal(0.5);
    private static List<String> winStockImageList;
    private static List<String> allWinStockImageList;
    private static List<String> lossStockImageList;

    public static boolean isLossImageProfitType(String code, List<StockDayData> stockDayDataList, StockDayData data) {
        //计算股价图形特征
        int stockImageValue = StockRangeUtils.cacullateStockImage(stockDayDataList, data);
        String stockImageProfitType = TulongdaoUtils.getStockImageProfitType(stockImageValue);
        //跳过亏损形态
        if (StockImageProfitTypeEnums.LOSS.equalsCode(stockImageProfitType)
            || StockImageProfitTypeEnums.ALL_LOSS.equalsCode(stockImageProfitType)) {
            log.info("{}-{}-stockImageProfitType is loss,stockImageValue:{}", code, data.getDate(), stockImageValue);
            return true;
        }
        return false;
    }

    public static String getStockImageProfitType(int stockImageValue) {
        String v = stockImageValue + "";
        if (winStockImageList.contains(v)) {
            return "win";
        }
        if (allWinStockImageList.contains(v)) {
            return "allWin";
        }
        if (lossStockImageList.contains(v)) {
            return "loss";
        }
        return "balance";
    }

    /**
     * 获取涨停日后的买点日期
     *
     * @param stockDayDataList
     * @param data
     * @return
     */
    public static StockDayData getSignalData(String code, List<StockDayData> stockDayDataList, StockDayData data,
        String earlyWarnFlag) {
        StockDayData match = null;
        int matchIndex = 0;
        List<TldMatchItem> openMatchItemList = null;
        List<TldMatchItem> matchItemList = null;
        List<TldMatchItem> crossItemList = null;
        TldMatchItem matchItem = null;
        BigDecimal beforeClosePrice = stockDayDataList.get(0).getClosePrice();
        for (int i = 0; i < stockDayDataList.size(); i++) {
            StockDayData d = stockDayDataList.get(i);
            if (isLimitDown(d)) {
                return null;
            }
            if (i == 0 && isLimitUp(d)) {
                return null;
            }
            //第一天涨超过7个点的忽略
            if (i == 0 && BigDecimalUtils.isGe(d.getRate(), seven)) {
                return null;
            }
            //跳过第一天
            if (i == 0) {
                continue;
            }
            //跌停板，跳过
            BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(beforeClosePrice, d.getMinPrice());
            if (BigDecimalUtils.isGe(rate, ninePointSix)) {
                log.info("{}-{}-跌停,跳过", code, d.getDate());
                continue;
            }
            openMatchItemList = getStockLineTldMatchItem(code, d, d.getOpenPrice(), beforeClosePrice, i, earlyWarnFlag);
            matchItemList = getStockLineTldMatchItem(code, d, d.getMinPrice(), beforeClosePrice, i, earlyWarnFlag);
            crossItemList = getCrossPeriods(d, d.getOpenPrice(), d.getMinPrice(), beforeClosePrice);
            matchItem = getMatchItem(openMatchItemList, matchItemList, crossItemList, d.getOpenPrice());
            if (Objects.nonNull(matchItem)) {
                match = d;
                matchIndex = i;
                break;
            }
            beforeClosePrice = d.getClosePrice();
        }
        if (Objects.isNull(match)) {
            return null;
        }
        //跌停板,不是买入点
        if (isLimitDown(match)) {
            return null;
        }
        //收盘价不能跌破涨停日的最低价
        if (BigDecimalUtils.isLt(match.getClosePrice(), data.getMinPrice())) {
            return null;
        }
        //统计数据：第3天跌超过6.5的胜率不高
        if (matchIndex == 2) {
            StockDayData beforeData = stockDayDataList.get(matchIndex - 1);
            BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(beforeData.getClosePrice(),
                match.getClosePrice());
            if (BigDecimalUtils.isGe(rate, sixPointFive)) {
                return null;
            }
        }
        TldMatchInfo matchInfo = new TldMatchInfo();
        matchInfo.setCode(code);
        matchInfo.setIndex(matchIndex);
        matchInfo.setOpenMatchItems(openMatchItemList);
        matchInfo.setMatchItems(matchItemList);
        matchInfo.setCrossItems(crossItemList);
        matchInfo.setMatchItem(BeanConvertUtils.convert(matchItem, TldMatchItem.class));
        matchInfo.setMatchDate(StringUtils.isNotBlank(match.getDatetime()) ? match.getDatetime() : match.getDate()+" 00:00:00");
        matchInfo.setOpenPrice(match.getOpenPrice());
        StockDayData matchData = BeanConvertUtils.convert(match, StockDayData.class);
        matchData.setFenshiList(null);
        matchInfo.setData(matchData);
        matchInfo.setEarlyWarnFlag(earlyWarnFlag);
        match.setTldMatchInfo(matchInfo);
        return match;
    }

    private static List<TldMatchItem> getStockLineTldMatchItem(String code, StockDayData data, BigDecimal price,
        BigDecimal beforeClosePrice, int index, String earlyWarnFlag) {
        //价格比昨日收盘价高1个点以上，直接不匹配
        BigDecimal itemBeforeCloseRate = BigDecimalUtils.subStractAndDividePrecent(price,
            beforeClosePrice);
        if (BigDecimalUtils.isGt(itemBeforeCloseRate, one)) {
            return null;
        }
        List<TldMatchItem> matchInfoList = new ArrayList<>();
        Integer maxPeriod = 10;
        BigDecimal maxAvg = getFixedAveragePrice(data, maxPeriod);
        BigDecimal compareRate = BooleanUtils.isTrue(earlyWarnFlag) ? twoPointTwoFive : onePointTwoFive;
        BigDecimal specialCompareRate = BooleanUtils.isTrue(earlyWarnFlag) ? three : twoPointTwoFive;
        for (Integer period : ALL_PERIODS) {
            BigDecimal avg = getFixedAveragePrice(data, period);
            if (BigDecimalUtils.isGt(avg, maxAvg)) {
                maxPeriod = period;
                maxAvg = avg;
            }
            BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(price, avg);
            //匹配均线
            if (BigDecimalUtils.isGt(rate.abs(), compareRate)) {
                continue;
            }

            TldMatchItem matchInfo = new TldMatchItem();
            matchInfo.setPeriod(period);
            matchInfo.setPrice(price);
            matchInfo.setAvg(avg);
            matchInfo.setRate(rate);
            matchInfo.setBuyPrice(BigDecimalUtils.multiply(avg, onePointZeroOneTwo));
            matchInfoList.add(matchInfo);
        }
        //特殊处理，第3天最高的10日均线与昨日收盘价差离超过5%，且股价跌落到10日均线2个点以内，则认为是买点
        if (CollectionUtils.isEmpty(matchInfoList) && index == 2) {
            BigDecimal downRate = BigDecimalUtils.subStractAndDividePrecent(beforeClosePrice, maxAvg);
            if (BigDecimalUtils.isGe(downRate, five) && maxPeriod == 10) {
                //匹配10日均线2个点范围内
                BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(price, maxAvg);
                if (BigDecimalUtils.isLe(rate.abs(), specialCompareRate)) {
                    TldMatchItem matchInfo = new TldMatchItem();
                    matchInfo.setPeriod(-1);
                    matchInfo.setPrice(price);
                    matchInfo.setAvg(maxAvg);
                    matchInfo.setRate(rate);
                    matchInfo.setBuyPrice(price);
                    matchInfoList.add(matchInfo);
                    log.info("{}-{}:tulongdao special match tenAvg 2% signal:{}", code, data.getDate(), matchInfo);
                }
            }
        }
        return matchInfoList;
    }

    private static BigDecimal getFixedAveragePrice(StockDayData data, Integer period) {
        BigDecimal avg = getAveragePrice(data, period);
        //计算差价平均,与均线相减
        BigDecimal priceRange = BigDecimalUtils.subtract(data.getClosePrice(), data.getMinPrice());
        BigDecimal avgRange = BigDecimalUtils.divide(priceRange, new BigDecimal(period.toString()), 3);
        return BigDecimalUtils.subtract(avg, avgRange);
    }

    private static List<TldMatchItem> getCrossPeriods(StockDayData data, BigDecimal openPrice, BigDecimal minPrice,
        BigDecimal beforeClosePrice) {

        List<TldMatchItem> matchItemList = new ArrayList<>();
        for (Entry<Integer, String> entry : PERIOD_MAP.entrySet()) {
            Integer period = entry.getKey();
            BigDecimal avg = getFixedAveragePrice(data, period);
            BigDecimal upPrice = BigDecimalUtils.multiply(openPrice, onePointZeroOneTwo);
            BigDecimal downPrice = BigDecimalUtils.multiply(minPrice, zeroNineEightEight);
            if (BigDecimalUtils.isLe(avg, upPrice) && BigDecimalUtils.isGe(avg, downPrice)) {
                //价格比昨日收盘价高1个点以上，直接不匹配
                BigDecimal itemBeforeCloseRate = BigDecimalUtils.subStractAndDividePrecent(avg,
                    beforeClosePrice);
                if (BigDecimalUtils.isGt(itemBeforeCloseRate, one)) {
                    return null;
                }
                TldMatchItem matchInfo = new TldMatchItem();
                matchInfo.setPeriod(period);
                matchInfo.setAvg(avg);
                matchInfo.setRate(onePointZeroOneTwo);
                matchInfo.setBuyPrice(BigDecimalUtils.multiply(avg, onePointZeroOneTwo));
                matchInfo.setPrice(avg);
                matchItemList.add(matchInfo);
            }

        }
        return matchItemList;
    }

    private static List<Integer> getStockLineEqualInfo(StockDayData data) {
        BigDecimal currentMinPrice = data.getMinPrice();
        List<Integer> periods = new ArrayList<>();
        for (Entry<Integer, String> entry : PERIOD_MAP.entrySet()) {
            Integer period = entry.getKey();
            if (BigDecimalUtils.eq(currentMinPrice, getAveragePrice(data, period))) {
                periods.add(period);
            }
        }
        return periods;
    }

    public static TldMatchItem getMatchItem(TldMatchInfo matchInfo) {
        return getMatchItem(matchInfo.getOpenMatchItems(), matchInfo.getMatchItems(), matchInfo.getCrossItems(),
            matchInfo.getOpenPrice());
    }

    public static TldMatchItem getMatchItem(List<TldMatchItem> openMatchItems, List<TldMatchItem> matchItems,
        List<TldMatchItem> crossItems, BigDecimal openPrice) {
        if (CollectionUtils.isNotEmpty(openMatchItems)) {
            if (openMatchItems.size() == 1) {
                //如果开盘只匹配了10日均线,且开盘价在10日线下方,忽略掉10日均线,且在选项里面也忽略掉
                TldMatchItem item = openMatchItems.get(0);
                if (item.getPeriod() == 10 && BigDecimalUtils.isGt(item.getAvg(), openPrice)) {
                    if (CollectionUtils.isNotEmpty(matchItems)) {
                        matchItems = matchItems.stream().filter(t -> !t.getPeriod().equals(10)).collect(
                            Collectors.toList());
                    }
                    if (CollectionUtils.isNotEmpty(crossItems)) {
                        crossItems = crossItems.stream().filter(t -> !t.getPeriod().equals(10)).collect(
                            Collectors.toList());
                    }
                }
            } else {
                sortItemList(openMatchItems);
                for (TldMatchItem item : openMatchItems) {
                    if (BigDecimalUtils.isLt(item.getAvg(), openPrice)) {
                        return item;
                    }
                }
            }
        }
        //穿过的均线匹配
        if (CollectionUtils.isNotEmpty(crossItems)) {
            sortItemList(crossItems);
            for (TldMatchItem item : crossItems) {
                if (BigDecimalUtils.isLt(item.getAvg(), openPrice)) {
                    return item;
                }
            }
        }
        //匹配最低价
        if (CollectionUtils.isNotEmpty(matchItems)) {
            sortItemList(matchItems);
            for (TldMatchItem item : matchItems) {
                if (BigDecimalUtils.isLt(item.getAvg(), openPrice)) {
                    return item;
                }
            }
        }
        return null;
    }

    private static void sortItemList(List<TldMatchItem> matchItems) {
        matchItems.sort((o1, o2) -> o2.getAvg().compareTo(o1.getAvg()));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        for (Integer period : ALL_PERIODS) {
            PERIOD_MAP.put(period, period + "日均线");
        }
        String stockImage = ClassUtils.getFileContentList("stockImageCode.json").get(0);
        JSONObject stockImageJson = JSON.parseObject(stockImage);
        winStockImageList = Lists.newArrayList(stockImageJson.getString("win").split(","));
        allWinStockImageList = Lists.newArrayList(stockImageJson.getString("allWin").split(","));
        lossStockImageList = Lists.newArrayList(stockImageJson.getString("loss").split(","));

    }

    public static void main(String[] args) {
        String s = "{\n" + "    \"earlyWarnFlag\": \"n\",\n" + "    \"matchDate\": \"2024-12-12 13:40:14\",\n"
            + "    \"code\": \"sh603015\",\n" + "    \"openMatchItems\": [\n" + "        {\n"
            + "            \"buyPrice\": 12.2,\n" + "            \"period\": 13,\n" + "            \"avg\": 12.06,\n"
            + "            \"rate\": 0,\n" + "            \"price\": 12.06,\n" + "            \"ignore\": false\n"
            + "        }\n" + "    ],\n" + "   \n" + "    \"match\": true,\n" + "    \"index\": 1,\n"
            + "    \"openPrice\": 12.06,\n" + "    \n" + "    \"matchItems\": [\n" + "\n" + "    ]\n" + "}";
        TldMatchInfo tldMatchInfo = JSON.parseObject(s, TldMatchInfo.class);
        TldMatchItem tldMatchItem = getMatchItem(tldMatchInfo.getOpenMatchItems(), tldMatchInfo.getMatchItems(),
            tldMatchInfo.getCrossItems(), tldMatchInfo.getOpenPrice());
        System.out.println(JSON.toJSONString(tldMatchItem));
    }
}
